---
title: GraphQL for Sui RPC (Beta)
beta: devnet, testnet, mainnet
description: Use GraphQL to make Sui RPC calls. This feature is currently in Beta. 
keywords: [ graphql, graphql headers, x-sui-rpc-version, x-sui-rpc-show-usage, variables, fragments, pagination, graphql limits, make rpc call, sui rpc calls ]
---

This section explains some of the common concepts when working with GraphQL, such as altering behavior using HTTP headers, re-using query snippets with variables and fragments, consuming paginated queries, and understanding and working within the limits enforced by the service.

Jump to the following sections for more details:

- [Headers](#headers)
- [Variables](#variables)
- [Fragments](#fragments)
- [Pagination](#pagination)
- [Limits](#limits)
- [Retention](#retention)

For more details on GraphQL fundamentals, see the introductory documentation from [GraphQL](https://graphql.org/learn/) and [GitHub](https://docs.github.com/en/graphql/guides/introduction-to-graphql).

:::info

<ImportContent source="data-serving-msg.mdx" mode="snippet" />

<ImportContent source="data-serving-gql-alpha.mdx" mode="snippet" />

:::

:::caution

Switch any apps that still use the GraphQL Alpha endpoints (`https://sui-mainnet.mystenlabs.com/graphql`, `https://sui-testnet.mystenlabs.com/graphql`, and `https://sui-devnet.mystenlabs.com/graphql`) to the GraphQL Beta endpoints as soon as possible to avoid disruption of service.

- GraphQL Beta Mainnet: https://graphql.mainnet.sui.io/graphql
- GraphQL Beta Testnet: https://graphql.testnet.sui.io/graphql
- GraphQL Beta Devnet: https://graphql.devnet.sui.io/graphql

:::

## Headers

The service accepts the following optional HTTP request headers:

- `x-sui-rpc-version` to specify which RPC version to use (currently only one version is supported),
- `x-sui-rpc-show-usage` returns the response with extra query complexity information.

By default, each response contains the following HTTP response headers:
- `x-sui-rpc-request-id` a unique identifier for the request; appears in application logs for debugging.
- `x-sui-rpc-version` the version of the service that handled the request.

```sh
$ curl -i -X POST https://graphql.testnet.sui.io/graphql\
     --header 'x-sui-rpc-show-usage: true'                 \
     --header 'Content-Type: application/json'             \
     --data '{
          "query": "query { epoch { referenceGasPrice } }"
     }'
```

The response for the previous request looks similar to the following:

```sh
HTTP/2 200 
content-type: application/json
content-length: 179
x-sui-rpc-request-id: f5442058-47ab-4360-8295-61c009f38516
x-sui-rpc-version: 1.56.1-
vary: origin, access-control-request-method, access-control-request-headers
access-control-allow-origin: *
date: Tue, 09 Sep 2025 23:34:04 GMT
via: 1.1 google
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

{
  "data": { 
    "epoch": { 
      "referenceGasPrice": "1000" 
    } 
  },
  "extensions": {
    "usage": {
      "input": { 
        "nodes": 2, 
        "depth": 2 
      },
      "payload": { 
        "query_payload_size": 67, 
        "tx_payload_size": 0 
      },
      "output": { 
        "nodes": 2 
      }
    }
  }
}
```

## Variables

Variables offer a way to introduce dynamic inputs to a re-usable/static query. Declare variables in the parameters to a `query` or `mutation`, using the `$` symbol and its type (in this example `Int`), which must be a `scalar`, `enum`, or `input` type. In the query body, refer to it by its name (prefixed with the `$` symbol).

If you declare a variable but don't use it or define it (supply a value) in the query, the query fails to execute.

To learn more, read the GraphQL documentation on [Variables](https://graphql.org/learn/queries/#variables).

In the following example, a variable supplies the ID of the epoch being queried.

```graphql
query ($epochID: Int) {
  epoch(id: $epochID) {
    referenceGasPrice
  }
}
```

**Variables**:
```json
{
   "epochID": 100
}
```

### In the IDE

When using the online IDE, supply variables as a JSON object to the query in the **Variables** pane at the bottom of the main editing window. You receive a warning if you supply a variable but don't declare it.

### In requests

When making a request to the GraphQL service (for example, using `curl`), pass the query and variables as two fields of a single JSON object:

```sh
$ curl -X POST https://sui-testnet.mystenlabs.com/graphql \
    --header 'Content-Type: application/json' \
    --data '{
      "query": "query ($epochID: Int) { epoch(id: $epochID) { referenceGasPrice } }",
      "variables": { "epochID": 100 }
  }'
```

## Fragments

Fragments are reusable units that you can include in queries as needed. To learn more, consult the official GraphQL [documentation](https://graphql.org/learn/queries/#fragments). The following example uses fragments to factor out a reusable snippet representing a Move value.

```graphql
query DynamicField {
  object(
    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
  ) {
    dynamicField(
      name: {
        type: "0x2::kiosk::Lock",
        bcs: "NLArx1UJguOUYmXgNG8Pv8KbKXLjWtCi6i0Yeq1Vhfw=",
      }
    ) {
      ...DynamicFieldSelect
    }
  }
}

fragment DynamicFieldSelect on DynamicField {
  name {
    ...MoveValueFields
  }
  value {
    ...DynamicFieldValueSelection
  }
}

fragment DynamicFieldValueSelection on DynamicFieldValue {
  __typename
  ... on MoveValue {
    ...MoveValueFields
  }
  ... on MoveObject {
    hasPublicTransfer
    contents {
      ...MoveValueFields
    }
  }
}

fragment MoveValueFields on MoveValue {
  type {
    repr
  }
  json
  bcs
}
```

## Pagination

GraphQL supports queries that fetch multiple kinds of potentially nested data. For example, the following query retrieves the first 10 transactions in epoch `97` along with the:

- Digest
- Sender's address
- Gas object returned after paying for the transaction
- Gas price
- Gas budget

```graphql
query {
  epoch(epochId: 97) {
    transactions(first: 10) {
      pageInfo {
        hasNextPage
        endCursor
      }
      nodes {
        digest
        sender {
          address
        }
        effects {
          gasEffects {
            gasObject {
              address
            }
          }
        }
        gasInput {
          gasPrice
          gasBudget
        }
      }
    }
  }
}
```

If there are too many transactions to return in a single response, the service applies a [limit](#limits) on the maximum page size for variable size responses (like the `transactionBlock` query) and you must fetch further results through [pagination](https://graphql.org/learn/pagination/).

### Connections

Fields that return a paginated response accept at least the following optional parameters:

- `first`, a limit on page size that is met by dropping excess results from the end.
- `after`, a cursor that bounds the results from below, exclusively.
- `last`, a limit on page size that is met by dropping excess results from the start.
- `before`, a cursor that bounds the results from above, exclusively.

They also return a type that conforms to the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm), meaning its name ends in `Connection`, and it contains at least the following fields:

- `pageInfo`, of type <UnsafeLink href="/references/sui-api/sui-graphql/beta/reference/types/objects/page-info">PageInfo</UnsafeLink>, which indicates whether there are more pages before or after the page returned.
- `nodes`, the content of the paginated response, as a list of the type being paginated (`TransactionBlock` in the previous example).
- `edges`, similar to `nodes` but associating each node with its [cursor](#cursors).

### Cursors

Cursors are opaque identifiers for paginated results. The only valid source for a cursor parameter (like `after` and `before`) is a cursor field from a previous paginated response (like `PageInfo.startCursor`, `PageInfo.endCursor`, or `Edge.cursor`). The underlying format of the cursor is an implementation detail, and is not guaranteed to remain fixed across versions of the GraphQL service, so do not rely on it -- generating cursors out of thin air is not expected or supported.

Cursors are used to bound results from below (with `after`) and above (with `before`). In both cases, the bound is exclusive, meaning it does not include the result that the cursor points to in the bounded region.

#### Consistency

Cursors for queries that paginate live objects also guarantee **consistent** pagination. 

If a paginated query reads the state of the network at a specific checkpoint (i.e., checkpoint `X`), the query returns cursors that can be used by subsequent requests. When a future call fetches the next page of results, it uses the previous query's cursors and continues reading from the network as it existed at checkpoint `X`, even if newer checkpoints are available. Currently, the available range offers roughly 1 hour to finish pagination.

This property requires that cursors that are used together (for example when supplying an `after` and `before` bound) are fixed on the same checkpoint, otherwise the query produces an error.

### Page limits

After results are bounded using cursors, a page size limit is applied using the `first` and `last` parameters. The service requires these parameters to be less than or equal to the max page size [limit](#limits), and if you provide neither, it selects a default. In addition to setting a limit, `first` and `last` control where excess elements are discarded from. For example, if there are `10` potential results -- `R0`, `R1`, ..., `R9` -- after cursor bounds have been applied, then

- a limit of `first: 3` would select `R0`, `R1`, `R2`, and
- a limit of `last: 3` would select `R7`, `R8`, `R9`.

:::info

It is an error to apply both a `first` and a `last` limit.

:::

### Examples

To see these principles put into practice, consult the examples for [paginating forwards](/guides/developer/advanced/graphql-rpc.mdx#page-forward) and [paginating backwards](/guides/developer/advanced/graphql-rpc.mdx#page-back) in the getting started guide.

## Limits

The GraphQL service for Sui RPC is rate-limited on all available instances to keep network throughput optimized and to protect against excessive or abusive calls to the service.

### Rate limits

Queries are rate-limited at the number of attempts per minute to ensure high availability of the service to all users.

### Query limits

In addition to rate limits, queries are also validated against a number of rules on their complexity, such as the number of nodes, the depth of the query, or their payload size. Query the `serviceConfig` field to retrieve these limits. An example of how to query for some of the available limits follows:

```graphql
{
  serviceConfig {
    maxQueryDepth
    maxQueryNodes
    maxOutputNodes
    defaultPageSize
    maxPageSize
    queryTimeoutMs
    maxQueryPayloadSize
    maxTypeArgumentDepth
    maxTypeArgumentWidth
    maxTypeNodes
    maxMoveValueDepth
  }
}
```

## Retention

Different queries are subject to their own retention policies. For example, live object set queries work only for recent checkpoints (measured in minutes or hours), while transaction pagination may be available only back to certain checkpoints (measured in weeks or months), depending on the filters applied. Data outside these ranges is pruned. You can query the upper- and lower-bounds of that range for your query as follows:

```graphql
{
  serviceConfig {
    retention(type: "Query", field: "transactions", filter: "affectedAddress") {
      first { sequenceNumber }
      last { sequenceNumber }
    }
  }
}
```

The results are the first and last checkpoint for which pagination continues to work and produce a consistent result.

## Related links

<RelatedLink to="/guides/developer/advanced/graphql-rpc.mdx" />
<RelatedLink to="/references/sui-graphql.mdx" />
<RelatedLink to="/concepts/data-access/graphql-indexer" />
